Setup
library(magrittr)
library(tidyverse)
library(glue)
library(arrow)
library(matric)
batch <- params$batch
futile.logger::flog.info(glue("Batch = {batch}"))
INFO [2021-04-17 12:15:27] Batch = 2020_11_04_CPJUMP1
experiment <- as.data.frame(params$experiment)
futile.logger::flog.info(glue("Experiment = {experiment}"))
INFO [2021-04-17 12:15:27] Experiment = Standard
INFO [2021-04-17 12:15:27] Experiment = Compound
INFO [2021-04-17 12:15:27] Experiment = U2OS
INFO [2021-04-17 12:15:27] Experiment = 48
data_level <- params$data_level
futile.logger::flog.info(glue("Data level = {data_level}"))
INFO [2021-04-17 12:15:27] Data level = normalized
if (params$normalization == "whole_plate") {
norm_suffix <- ""
} else if (params$normalization == "negcon") {
norm_suffix <- "_negcon"
}
sprintf("Normalization = %s. Using suffix = '%s'", params$normalization, norm_suffix)
[1] "Normalization = negcon. Using suffix = '_negcon'"
filename_prefix_profiles <- glue("{batch}_all_{data_level}{norm_suffix}")
Load
parquet_file_recoded <-
glue("../collated/{batch}/{filename_prefix_profiles}_recoded_aggregated.parquet")
if (file.exists(parquet_file_recoded)) {
parquet_file <- parquet_file_recoded
futile.logger::flog.info(glue("Loading {parquet_file} ..."))
stopifnot(file.exists(parquet_file))
profiles <-
read_parquet(parquet_file)
variables <-
str_subset(names(profiles), "Metadata_", negate = TRUE)
} else {
parquet_file <-
glue("../collated/{batch}/{filename_prefix_profiles}.parquet")
futile.logger::flog.info(glue("Loading {parquet_file} ..."))
stopifnot(file.exists(parquet_file))
profiles <-
read_parquet(parquet_file)
if(!is.null(experiment)) {
profiles <- profiles %>% inner_join(experiment)
}
variables <-
str_subset(names(profiles), "Metadata_", negate = TRUE)
metadata_cols <-
str_subset(names(profiles), "Metadata_")
variables <-
params$variable_groups %>%
unlist() %>%
map(function(pattern)
str_subset(variables, pattern = pattern)) %>%
unlist()
profiles <-
profiles %>%
select(all_of(c(metadata_cols, variables)))
variables <-
str_subset(names(profiles), "Metadata_", negate = TRUE)
profiles <-
profiles %>%
group_by(across(params$strata_replicate)) %>%
summarize(across(all_of(variables), median),
.groups = "keep")
attr(profiles, "variable_groups") <- params$variable_groups
parquet_file <-
glue("../collated/{batch}/{filename_prefix_profiles}_recoded_aggregated.parquet")
futile.logger::flog.info(glue("Writing {parquet_file_recoded} ..."))
profiles %>%
write_parquet(parquet_file,
compression = "gzip",
compression_level = 9)
}
INFO [2021-04-17 12:15:28] Loading ../collated/2020_11_04_CPJUMP1/2020_11_04_CPJUMP1_all_normalized_negcon.parquet ...
Joining, by = c("Metadata_cell_line", "Metadata_timepoint", "Metadata_experiment_type", "Metadata_experiment_condition")
INFO [2021-04-17 12:16:21] Writing ../collated/2020_11_04_CPJUMP1/2020_11_04_CPJUMP1_all_normalized_negcon_recoded_aggregated.parquet ...
Plot
profiles_enriched %>%
pivot_longer(-matches("Metadata"),
names_to = "feature_group")%>%
ggplot(aes(feature_group, value)) +
geom_boxplot() +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))

profiles_enriched %>%
pivot_longer(-matches("Metadata"),
names_to = "feature_group") %>%
ggplot(aes(feature_group, value, group = Metadata_Well)) +
geom_line(alpha = .09) +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
facet_wrap(~Metadata_negcon_or_other, ncol = 1)

library(plotly)
df <- profiles_enriched
dimensions <- list(
list(label = "Intensity_RNA",
values = ~ Intensity_RNA),
list(label = 'Intensity_DNA',
values = ~ Intensity_DNA)
)
df <- df %>% select(all_of(variables_enriched))
dimensions <-
variables_enriched %>%
map(function(variable) {
list(label = variable,
values = df[[variable]])
})
df %>%
plot_ly(width = 1000,
height = 600,
hoverinfo = "text") %>%
add_trace(type = 'parcoords',
dimensions = dimensions)
'parcoords' objects don't have these attributes: 'hoverinfo'
Valid attributes include:
'type', 'visible', 'name', 'uid', 'ids', 'customdata', 'meta', 'stream', 'transforms', 'uirevision', 'domain', 'labelangle', 'labelside', 'labelfont', 'tickfont', 'rangefont', 'dimensions', 'line', 'idssrc', 'customdatasrc', 'metasrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'
'parcoords' objects don't have these attributes: 'hoverinfo'
Valid attributes include:
'type', 'visible', 'name', 'uid', 'ids', 'customdata', 'meta', 'stream', 'transforms', 'uirevision', 'domain', 'labelangle', 'labelside', 'labelfont', 'tickfont', 'rangefont', 'dimensions', 'line', 'idssrc', 'customdatasrc', 'metasrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'
Save
attr(profiles, "variable_groups") <- params$variable_groups
parquet_file <-
glue("../collated/{batch}/{filename_prefix_profiles}_enriched.parquet")
futile.logger::flog.info(glue("Writing {parquet_file_recoded} ..."))
INFO [2021-04-17 12:17:04] Writing ../collated/2020_11_04_CPJUMP1/2020_11_04_CPJUMP1_all_normalized_negcon_recoded_aggregated.parquet ...
profiles_enriched %>%
write_parquet(parquet_file,
compression = "gzip",
compression_level = 9)
LS0tCnRpdGxlOiAiRmVhdHVyZSBhbmFseXNpcyIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogMwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRoZW1lOiBsdW1lbgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAnMycKICAgIGRmX3ByaW50OiBwYWdlZApwYXJhbXM6CiAgYmF0Y2g6IDIwMjBfMTFfMDRfQ1BKVU1QMQogIGRhdGFfbGV2ZWw6IG5vcm1hbGl6ZWQKICBub3JtYWxpemF0aW9uOiBuZWdjb24KICBleHBlcmltZW50OiAKICAgIHZhbHVlOgogICAgICBNZXRhZGF0YV9leHBlcmltZW50X2NvbmRpdGlvbjogU3RhbmRhcmQKICAgICAgTWV0YWRhdGFfZXhwZXJpbWVudF90eXBlOiBDb21wb3VuZAogICAgICBNZXRhZGF0YV9jZWxsX2xpbmU6IFUyT1MKICAgICAgTWV0YWRhdGFfdGltZXBvaW50OiAiNDgiCiAgbmVzdGluZ19sZXZlbF8wOgogICAgdmFsdWU6CiAgICAgIC0gTWV0YWRhdGFfZXhwZXJpbWVudF9jb25kaXRpb24KICAgICAgLSBNZXRhZGF0YV9leHBlcmltZW50X3R5cGUKICAgICAgLSBNZXRhZGF0YV9jZWxsX2xpbmUKICAgICAgLSBNZXRhZGF0YV90aW1lcG9pbnQKICBzdHJhdGFfcmVwbGljYXRlOgogICAgdmFsdWU6CiAgICAgIC0gTWV0YWRhdGFfZXhwZXJpbWVudF9jb25kaXRpb24KICAgICAgLSBNZXRhZGF0YV9leHBlcmltZW50X3R5cGUKICAgICAgLSBNZXRhZGF0YV9jZWxsX2xpbmUKICAgICAgLSBNZXRhZGF0YV90aW1lcG9pbnQKICAgICAgLSBNZXRhZGF0YV9wbGF0ZV9tYXBfbmFtZQogICAgICAtIE1ldGFkYXRhX1dlbGwKICAgICAgLSBNZXRhZGF0YV9nZW5lcwogICAgICAtIE1ldGFkYXRhX3BlcnRfdHlwZQogICAgICAtIE1ldGFkYXRhX2NvbnRyb2xfdHlwZQogICAgICAtIE1ldGFkYXRhX1BsYXRlX01hcF9OYW1lCiAgICAgIC0gTWV0YWRhdGFfbmVnY29uX2NvbnRyb2xfdHlwZQogICAgICAtIE1ldGFkYXRhX3RhcmdldF9zZXF1ZW5jZQogICAgICAtIE1ldGFkYXRhX21nX3Blcl9tbAogICAgICAtIE1ldGFkYXRhX21tb2xlc19wZXJfbGl0ZXIKICAgICAgLSBNZXRhZGF0YV9zb2x2ZW50CiAgICAgIC0gTWV0YWRhdGFfdGFyZ2V0CiAgICAgIC0gTWV0YWRhdGFfcGVydF9pbmFtZQogICAgICAtIE1ldGFkYXRhX3B1YmNoZW1fY2lkCiAgICAgIC0gTWV0YWRhdGFfSW5DaElLZXkKICAgICAgLSBNZXRhZGF0YV9nZW5lCiAgICAgIC0gTWV0YWRhdGFfbmVnY29uX29yX290aGVyCiAgICAgIC0gTWV0YWRhdGFfbmVnY29uX2NvbnRyb2xfdHlwZV90cmltbWVkCiAgdmFyaWFibGVfZ3JvdXBzOgogICAgdmFsdWU6CiAgICAgIC0geEFyZWE6IF9BcmVhU2hhcGVfQXJlYSQKICAgICAgLSB4U2hhcGU6IEFyZWFTaGFwZQogICAgICAtIHhOZWlnaDogTmVpZ2hib3JzCiAgICAgIC0geENvcnI6IENvcnJlbGF0aW9uCiAgICAgIC0gVGV4X0FHUDogKChUZXh0dXJlfEdyYW51bGFyaXR5fFJhZGlhbERpc3RyaWJ1dGlvbilfLipfKEFHUCkpCiAgICAgIC0gVGV4X0ROQTogKChUZXh0dXJlfEdyYW51bGFyaXR5fFJhZGlhbERpc3RyaWJ1dGlvbilfLipfKEROQSkpCiAgICAgIC0gVGV4X0VSOiAoKFRleHR1cmV8R3JhbnVsYXJpdHl8UmFkaWFsRGlzdHJpYnV0aW9uKV8uKl8oRVIpKQogICAgICAtIFRleF9NaXRvOiAoKFRleHR1cmV8R3JhbnVsYXJpdHl8UmFkaWFsRGlzdHJpYnV0aW9uKV8uKl8oTWl0bykpCiAgICAgIC0gVGV4X1JOQTogKChUZXh0dXJlfEdyYW51bGFyaXR5fFJhZGlhbERpc3RyaWJ1dGlvbilfLipfKFJOQSkpCiAgICAgIC0gSW50X0FHUDogKChJbnRlbnNpdHkpXy4qXyhBR1ApKQogICAgICAtIEludF9ETkE6ICgoSW50ZW5zaXR5KV8uKl8oRE5BKSkKICAgICAgLSBJbnRfRVI6ICgoSW50ZW5zaXR5KV8uKl8oRVIpKQogICAgICAtIEludF9NaXRvOiAoKEludGVuc2l0eSlfLipfKE1pdG8pKQogICAgICAtIEludF9STkE6ICgoSW50ZW5zaXR5KV8uKl8oUk5BKSkKLS0tCgojIFNldHVwCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnbHVlKQpsaWJyYXJ5KGFycm93KQpsaWJyYXJ5KG1hdHJpYykKYGBgCgoKYGBge3J9CmJhdGNoIDwtIHBhcmFtcyRiYXRjaApmdXRpbGUubG9nZ2VyOjpmbG9nLmluZm8oZ2x1ZSgiQmF0Y2ggPSB7YmF0Y2h9IikpCmBgYApgYGB7cn0KZXhwZXJpbWVudCA8LSBhcy5kYXRhLmZyYW1lKHBhcmFtcyRleHBlcmltZW50KQpmdXRpbGUubG9nZ2VyOjpmbG9nLmluZm8oZ2x1ZSgiRXhwZXJpbWVudCA9IHtleHBlcmltZW50fSIpKQpgYGAKCgpgYGB7cn0KZGF0YV9sZXZlbCA8LSBwYXJhbXMkZGF0YV9sZXZlbApmdXRpbGUubG9nZ2VyOjpmbG9nLmluZm8oZ2x1ZSgiRGF0YSBsZXZlbCA9IHtkYXRhX2xldmVsfSIpKQpgYGAKCgpgYGB7cn0KaWYgKHBhcmFtcyRub3JtYWxpemF0aW9uID09ICJ3aG9sZV9wbGF0ZSIpIHsKICBub3JtX3N1ZmZpeCA8LSAiIgp9IGVsc2UgaWYgKHBhcmFtcyRub3JtYWxpemF0aW9uID09ICJuZWdjb24iKSB7CiAgbm9ybV9zdWZmaXggPC0gIl9uZWdjb24iCn0Kc3ByaW50ZigiTm9ybWFsaXphdGlvbiA9ICVzLiBVc2luZyBzdWZmaXggPSAnJXMnIiwgcGFyYW1zJG5vcm1hbGl6YXRpb24sIG5vcm1fc3VmZml4KQpgYGAKCgpgYGB7cn0KZmlsZW5hbWVfcHJlZml4X3Byb2ZpbGVzIDwtIGdsdWUoIntiYXRjaH1fYWxsX3tkYXRhX2xldmVsfXtub3JtX3N1ZmZpeH0iKQpgYGAKCiMgTG9hZAoKYGBge3J9CnBhcnF1ZXRfZmlsZV9yZWNvZGVkIDwtCiAgZ2x1ZSgiLi4vY29sbGF0ZWQve2JhdGNofS97ZmlsZW5hbWVfcHJlZml4X3Byb2ZpbGVzfV9yZWNvZGVkX2FnZ3JlZ2F0ZWQucGFycXVldCIpCgppZiAoZmlsZS5leGlzdHMocGFycXVldF9maWxlX3JlY29kZWQpKSB7CiAgcGFycXVldF9maWxlIDwtIHBhcnF1ZXRfZmlsZV9yZWNvZGVkCiAgCiAgZnV0aWxlLmxvZ2dlcjo6ZmxvZy5pbmZvKGdsdWUoIkxvYWRpbmcge3BhcnF1ZXRfZmlsZX0gLi4uIikpCiAgCiAgc3RvcGlmbm90KGZpbGUuZXhpc3RzKHBhcnF1ZXRfZmlsZSkpCiAgCiAgcHJvZmlsZXMgPC0KICAgIHJlYWRfcGFycXVldChwYXJxdWV0X2ZpbGUpCiAgCiAgdmFyaWFibGVzIDwtCiAgICBzdHJfc3Vic2V0KG5hbWVzKHByb2ZpbGVzKSwgIk1ldGFkYXRhXyIsIG5lZ2F0ZSA9IFRSVUUpCiAgCn0gZWxzZSB7CiAgCiAgcGFycXVldF9maWxlIDwtCiAgICBnbHVlKCIuLi9jb2xsYXRlZC97YmF0Y2h9L3tmaWxlbmFtZV9wcmVmaXhfcHJvZmlsZXN9LnBhcnF1ZXQiKQogIAogIGZ1dGlsZS5sb2dnZXI6OmZsb2cuaW5mbyhnbHVlKCJMb2FkaW5nIHtwYXJxdWV0X2ZpbGV9IC4uLiIpKQogIAogIHN0b3BpZm5vdChmaWxlLmV4aXN0cyhwYXJxdWV0X2ZpbGUpKQogIAogIHByb2ZpbGVzIDwtCiAgICByZWFkX3BhcnF1ZXQocGFycXVldF9maWxlKQogIAogIGlmKCFpcy5udWxsKGV4cGVyaW1lbnQpKSB7CiAgICBwcm9maWxlcyA8LSBwcm9maWxlcyAlPiUgaW5uZXJfam9pbihleHBlcmltZW50KQogIH0KICAKICB2YXJpYWJsZXMgPC0KICAgIHN0cl9zdWJzZXQobmFtZXMocHJvZmlsZXMpLCAiTWV0YWRhdGFfIiwgbmVnYXRlID0gVFJVRSkKICAKICBtZXRhZGF0YV9jb2xzIDwtCiAgICBzdHJfc3Vic2V0KG5hbWVzKHByb2ZpbGVzKSwgIk1ldGFkYXRhXyIpCiAgCiAgdmFyaWFibGVzIDwtCiAgICBwYXJhbXMkdmFyaWFibGVfZ3JvdXBzICU+JQogICAgdW5saXN0KCkgJT4lCiAgICBtYXAoZnVuY3Rpb24ocGF0dGVybikKICAgICAgc3RyX3N1YnNldCh2YXJpYWJsZXMsIHBhdHRlcm4gPSBwYXR0ZXJuKSkgJT4lCiAgICB1bmxpc3QoKQogIAogIHByb2ZpbGVzIDwtCiAgICBwcm9maWxlcyAlPiUKICAgIHNlbGVjdChhbGxfb2YoYyhtZXRhZGF0YV9jb2xzLCB2YXJpYWJsZXMpKSkKICAKICB2YXJpYWJsZXMgPC0KICAgIHN0cl9zdWJzZXQobmFtZXMocHJvZmlsZXMpLCAiTWV0YWRhdGFfIiwgbmVnYXRlID0gVFJVRSkKICAKICBwcm9maWxlcyA8LQogICAgcHJvZmlsZXMgJT4lCiAgICBncm91cF9ieShhY3Jvc3MocGFyYW1zJHN0cmF0YV9yZXBsaWNhdGUpKSAlPiUKICAgIHN1bW1hcml6ZShhY3Jvc3MoYWxsX29mKHZhcmlhYmxlcyksIG1lZGlhbiksCiAgICAgICAgICAgICAgLmdyb3VwcyA9ICJrZWVwIikKICAKICBhdHRyKHByb2ZpbGVzLCAidmFyaWFibGVfZ3JvdXBzIikgPC0gcGFyYW1zJHZhcmlhYmxlX2dyb3VwcwogIAogIHBhcnF1ZXRfZmlsZSA8LQogICAgZ2x1ZSgiLi4vY29sbGF0ZWQve2JhdGNofS97ZmlsZW5hbWVfcHJlZml4X3Byb2ZpbGVzfV9yZWNvZGVkX2FnZ3JlZ2F0ZWQucGFycXVldCIpCiAgCiAgZnV0aWxlLmxvZ2dlcjo6ZmxvZy5pbmZvKGdsdWUoIldyaXRpbmcge3BhcnF1ZXRfZmlsZV9yZWNvZGVkfSAuLi4iKSkKICAKICBwcm9maWxlcyAlPiUKICAgIHdyaXRlX3BhcnF1ZXQocGFycXVldF9maWxlLAogICAgICAgICAgICAgICAgICBjb21wcmVzc2lvbiA9ICJnemlwIiwKICAgICAgICAgICAgICAgICAgY29tcHJlc3Npb25fbGV2ZWwgPSA5KQp9CmBgYAoKIyBUcmFuc2Zvcm0KCiMjIEZ1bmN0aW9ucwoKYGBge3J9CmdldF9iZXRhIDwtIGZ1bmN0aW9uKHggPSA1LCB5ID0gMC45OSkgewogIC1sb2coMSAvIHkgLSAxKSAvIHgKfQoKYWJzX2xvZ2lzdGljIDwtIGZ1bmN0aW9uKHgsIGJldGEgPSBnZXRfYmV0YSgpKSB7CiAgMiAvICgxICsgZXhwKC1hYnMoeCkgKiBiZXRhKSkgLSAxCn0KCmN5dG9taW5lcl92YXJpYWJsZV9ncm91cF9lbnJpY2htZW50IDwtCiAgZnVuY3Rpb24ocG9wdWxhdGlvbiwKICAgICAgICAgICB2YXJpYWJsZXMsCiAgICAgICAgICAgdmFyaWFibGVfZ3JvdXBzLAogICAgICAgICAgIHNpZ21vaWRfZnVuY3Rpb24gPSBhYnNfbG9naXN0aWMsCiAgICAgICAgICAgLi4uKSB7CiAgICAKICAgIHZhcmlhYmxlc19ncm91cF9saXN0cyA8LQogICAgICB2YXJpYWJsZV9ncm91cHMgJT4lCiAgICAgIHVubGlzdCgpICU+JQogICAgICBuYW1lcygpICU+JQogICAgICBzZXRfbmFtZXMoKSAlPiUKICAgICAgcHVycnI6Om1hcChmdW5jdGlvbih2YXJpYWJsZV9ncm91cCkgewogICAgICAgIHN0cmluZ3I6OnN0cl9zdWJzZXQodmFyaWFibGVzLCB2YXJpYWJsZV9ncm91cCkKICAgICAgfSkgCgogICAgcG9wdWxhdGlvbl9kYXRhX3RyYW5zZm9ybWVkIDwtCiAgICAgIHZhcmlhYmxlc19ncm91cF9saXN0cyAlPiUKICAgICAgbWFwX2RmYyhmdW5jdGlvbih2YXJpYWJsZXNfZ3JvdXBfbGlzdCkgewogICAgICAgIHBvcHVsYXRpb24gJT4lCiAgICAgICAgICBkcGx5cjo6c2VsZWN0KGFsbF9vZih2YXJpYWJsZXNfZ3JvdXBfbGlzdCkpICU+JQogICAgICAgICAgZHBseXI6Om11dGF0ZShhY3Jvc3MoYWxsX29mKHZhcmlhYmxlc19ncm91cF9saXN0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZ21vaWRfZnVuY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuLi4pKSAlPiUKICAgICAgICAgIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgICAgICAgICBkcGx5cjo6cm93d2lzZSgpICU+JQogICAgICAgICAgZHBseXI6OnRyYW5zbXV0ZShuX2Fib3ZlID0gcm91bmQoc3VtKAogICAgICAgICAgICBkcGx5cjo6Y19hY3Jvc3MoZXZlcnl0aGluZygpKSAvCiAgICAgICAgICAgICAgbGVuZ3RoKHZhcmlhYmxlc19ncm91cF9saXN0KSwKICAgICAgICAgICAgbmEucm0gPSBUCiAgICAgICAgICApLCAyKSkgJT4lCiAgICAgICAgICBkcGx5cjo6cHVsbChuX2Fib3ZlKQogICAgICB9KQogICAgCiAgICBwb3B1bGF0aW9uX21ldGFkYXRhIDwtCiAgICAgIHBvcHVsYXRpb24gJT4lCiAgICAgIGRwbHlyOjpzZWxlY3QoLWFsbF9vZih2YXJpYWJsZXMpKQogICAgCiAgICBlbnJpY2hlZCA8LQogICAgICBkcGx5cjo6YmluZF9jb2xzKHBvcHVsYXRpb25fbWV0YWRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgcG9wdWxhdGlvbl9kYXRhX3RyYW5zZm9ybWVkKQogICAgCiAgICBlbnJpY2hlZAogICAgCiAgfQpgYGAKCiMjIEV4ZWN1dGUKCmBgYHtyfQpwcm9maWxlc19lbnJpY2hlZCA8LQogIHByb2ZpbGVzICU+JQogIGdyb3VwX2J5KGFjcm9zcyhwYXJhbXMkc3RyYXRhX3JlcGxpY2F0ZSkpICU+JQogIHN1bW1hcml6ZShhY3Jvc3MoYWxsX29mKHZhcmlhYmxlcyksIG1lZGlhbiksIC5ncm91cHMgPSAia2VlcCIpICU+JQogIGdyb3VwX2J5KGFjcm9zcyhwYXJhbXMkbmVzdGluZ19sZXZlbF8wKSkgJT4lCiAgc3VtbWFyaXNlKAogICAgY3l0b21pbmVyX3ZhcmlhYmxlX2dyb3VwX2VucmljaG1lbnQoCiAgICAgIGN1cl9kYXRhX2FsbCgpLAogICAgICB2YXJpYWJsZXMgPSB2YXJpYWJsZXMsCiAgICAgIHZhcmlhYmxlX2dyb3VwcyA9IHBhcmFtcyR2YXJpYWJsZV9ncm91cHMKICAgICksCiAgICAuZ3JvdXBzID0gImtlZXAiCiAgKSAlPiUKICB1bmdyb3VwKCkKCnZhcmlhYmxlc19lbnJpY2hlZCA8LQogIHN0cl9zdWJzZXQobmFtZXMocHJvZmlsZXNfZW5yaWNoZWQpLCAiTWV0YWRhdGFfIiwgbmVnYXRlID0gVFJVRSkKYGBgCgojIFBsb3QKCmBgYHtyfQpwcm9maWxlc19lbnJpY2hlZCAlPiUgCiAgcGl2b3RfbG9uZ2VyKC1tYXRjaGVzKCJNZXRhZGF0YSIpLCAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiZmVhdHVyZV9ncm91cCIpJT4lIAogIGdncGxvdChhZXMoZmVhdHVyZV9ncm91cCwgdmFsdWUpKSArIAogIGdlb21fYm94cGxvdCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKQpgYGAKYGBge3J9CnByb2ZpbGVzX2VucmljaGVkICU+JSAKICBwaXZvdF9sb25nZXIoLW1hdGNoZXMoIk1ldGFkYXRhIiksIAogICAgICAgICAgICAgICBuYW1lc190byA9ICJmZWF0dXJlX2dyb3VwIikgJT4lIAogIGdncGxvdChhZXMoZmVhdHVyZV9ncm91cCwgdmFsdWUsIGdyb3VwID0gTWV0YWRhdGFfV2VsbCkpICsgCiAgZ2VvbV9saW5lKGFscGhhID0gLjA5KSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkgKyAKICBmYWNldF93cmFwKH5NZXRhZGF0YV9uZWdjb25fb3Jfb3RoZXIsIG5jb2wgPSAxKQpgYGAKYGBge3J9CmxpYnJhcnkocGxvdGx5KQoKZGYgPC0gcHJvZmlsZXNfZW5yaWNoZWQKCmRpbWVuc2lvbnMgPC0gbGlzdCgKICBsaXN0KGxhYmVsID0gIkludGVuc2l0eV9STkEiLAogICAgICAgdmFsdWVzID0gfiBJbnRlbnNpdHlfUk5BKSwKICBsaXN0KGxhYmVsID0gJ0ludGVuc2l0eV9ETkEnLAogICAgICAgdmFsdWVzID0gfiBJbnRlbnNpdHlfRE5BKQopCgogIApkZiA8LSBkZiAlPiUgc2VsZWN0KGFsbF9vZih2YXJpYWJsZXNfZW5yaWNoZWQpKQoKZGltZW5zaW9ucyA8LQogIHZhcmlhYmxlc19lbnJpY2hlZCAlPiUKICBtYXAoZnVuY3Rpb24odmFyaWFibGUpIHsKICAgIGxpc3QobGFiZWwgPSB2YXJpYWJsZSwKICAgICAgICAgdmFsdWVzID0gZGZbW3ZhcmlhYmxlXV0pCiAgfSkKCmRmICU+JQogIHBsb3RfbHkod2lkdGggPSAxMDAwLCAKICAgICAgICAgIGhlaWdodCA9IDYwMCwKICAgICAgICAgIGhvdmVyaW5mbyA9ICJ0ZXh0IikgJT4lCiAgYWRkX3RyYWNlKHR5cGUgPSAncGFyY29vcmRzJywKICAgICAgICAgICAgZGltZW5zaW9ucyA9IGRpbWVuc2lvbnMpCmBgYAoKIyBTYXZlCgpgYGB7cn0KYXR0cihwcm9maWxlcywgInZhcmlhYmxlX2dyb3VwcyIpIDwtIHBhcmFtcyR2YXJpYWJsZV9ncm91cHMKCnBhcnF1ZXRfZmlsZSA8LQogIGdsdWUoIi4uL2NvbGxhdGVkL3tiYXRjaH0ve2ZpbGVuYW1lX3ByZWZpeF9wcm9maWxlc31fZW5yaWNoZWQucGFycXVldCIpCgpmdXRpbGUubG9nZ2VyOjpmbG9nLmluZm8oZ2x1ZSgiV3JpdGluZyB7cGFycXVldF9maWxlX3JlY29kZWR9IC4uLiIpKQoKcHJvZmlsZXNfZW5yaWNoZWQgJT4lCiAgd3JpdGVfcGFycXVldChwYXJxdWV0X2ZpbGUsCiAgICAgICAgICAgICAgICBjb21wcmVzc2lvbiA9ICJnemlwIiwKICAgICAgICAgICAgICAgIGNvbXByZXNzaW9uX2xldmVsID0gOSkKYGBgCgoK